package priv.conceit.sc.hoxton.file.handle.source;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.multipart.MultipartFile;
import priv.conceit.sc.hoxton.common.cache.util.RedisExecuteUtil;
import priv.conceit.sc.hoxton.common.constant.cache.CachePrefixKeyEnum;
import priv.conceit.sc.hoxton.common.entity.file.dto.FilePathDTO;
import priv.conceit.sc.hoxton.common.entity.file.dto.FilePreviewDTO;
import priv.conceit.sc.hoxton.common.util.RandomUtil;
import priv.conceit.sc.hoxton.file.config.FileProperties;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.File;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

/**
 * sc-hoxton 文件源抽象类 工厂模式
 *
 * @author Conceit
 * 2020/5/12 增加方法
 * @since 1.0.0, 2020/5/12
 */
@Validated
@Slf4j
public abstract class AbsSourceHandle {

	@Autowired
	FileProperties fileProperties;

	@Autowired
	RedisTemplate redisTemplate;

	@Autowired
	private RedisExecuteUtil redisExecuteUtil;


	/**
	 * 上传文件
	 *
	 * @param file 文件实体
	 * @return FilePathDTO 文件路径信息封装
	 * @author conceit
	 * @date 2020/5/15 21:21
	 */
	public abstract FilePathDTO upload(File file);

	/**
	 * 上传文件
	 *
	 * @param file {@link org.springframework.web.multipart}
	 * @return FilePathDTO 文件路径信息封装
	 * @author conceit
	 * @date 2020/5/15 21:21
	 */
	public abstract FilePathDTO upload(MultipartFile file) throws Exception;

	/**
	 * 删除文件
	 *
	 * @param  savedFileName 文件实际保存名
	 * @return boolean
	 * @author conceit
	 * @date 2020/5/15 21:21
	 */
	public abstract boolean del(String savedFileName) throws Exception;

	/**
	 * 预览文件
	 *
	 * @param savedFileName 文件实际保存名
	 * @return FilePreviewDTO 文件预览封装的信息
	 * @author conceit
	 * @date 2020/5/15 21:21
	 */
	public abstract FilePreviewDTO preview(String savedFileName)throws Exception;

	/**
	 * 下载文件
	 *
	 * @param savedFileName 文件实际保存名
	 * @return byte[]
	 * @author conceit
	 * @date 2020/5/15 21:21
	 */
	public abstract byte[] download(String savedFileName) throws Exception;

	/**
	 * 生成 真实保存的文件名
	 *
	 * @param filePathDTO 封装的中间路径信息
	 * @param originalName 文件提交名称
	 * @return FilePathDetail 将生成的文件路径信息 封装回去
	 * @author conceit
	 * @date 2020/5/15 21:21
	 */
	protected abstract FilePathDTO buildSavedFileName(@NotNull FilePathDTO filePathDTO,@NotBlank String originalName);

	/**
	 * 生成 真实保存中间文件夹名 由一级和二级目录合并而成
	 * 或者叫卷名 组名（fastDfs）  实际上也就是文件夹名 =_=!!
	 *
	 * @param localDateTime 传入的时间
	 * @return FilePathDTO 封装的中间路径信息
	 * @author conceit
	 * @date 2020/5/15 22:05
	 */
	protected abstract FilePathDTO buildSavedMidPathName(LocalDateTime localDateTime);

	/**
	 * 解析已保存的文件名 为相关文件路径等细节
	 *
	 * @param savedFileName 文件提交名称
	 * @return FilePathDTO
	 * @author conceit
	 * @date 2020/5/15 21:21
	 */
	public abstract FilePathDTO parseSavedFileName(String savedFileName);

	/**
	 * generateShortCode
	 * 当前日在当前年中的天数+指定位随机字符
	 * 4位概率约为366x32^4(约1048万)=1/36亿
	 * 将此key设置到redis的set中，每天清除。
	 *
	 * @return java.lang.String
	 * @author conceit
	 * @date 2020/5/20 11:40
	 */
	@SuppressWarnings("all")
	public String generateShortCode(){

		String shortCode= doGenerateShortCode();
		String key=redisExecuteUtil.buildProjectKey(CachePrefixKeyEnum.FILE_SHORT_CODE,shortCode);
		boolean isExist=redisTemplate.hasKey(key);
		while (isExist){
			log.info("生成ShortCode->[{}],出现重复！正在重新生成！");
			shortCode= doGenerateShortCode();
			isExist=redisTemplate.opsForValue().setIfAbsent(key,null,1, TimeUnit.DAYS);
		}

		return shortCode;
	}

	/**
	 * 具体的生成随机码方法
	 *
	 * @return java.lang.String
	 * @author conceit
	 * @date 2020/5/20 11:40
	 */
	private String doGenerateShortCode(){
		String shortCode= RandomUtil.getShortCode(fileProperties.getShortCode().getRandomLength());
		int dayOfYear= LocalDate.now().getDayOfYear();
		StringBuilder stringBuilder=new StringBuilder();
		stringBuilder.append(dayOfYear);
		stringBuilder.append(shortCode);
		return stringBuilder.toString();
	}


}
